// 原则：只要数据类型表示某种可迭代的结构，就应该实现为可迭代对象
// 实现：为了让类可迭代，必须实现一个Symbol.iterator的方法，该方法必须返回一个迭代器对象，该对象有一个next()方法，next()方法必须返回一个有value和done属性的对象
// 操作： 可迭代的数值Range类
/*
Range对象表示一个数值范围{x: form<=x<=to} Range定义了has()方法，用于测试给定数值是不是该范围的成员 Range是可迭代的，迭代其范围内的所有整数 */

class Range {
    constructor(from, to) {
        this.from = from
        this.to = to
    }
    has(x) {
        return typeof x === 'number' &&
            x >= this.from &&
            x <= this.to
    }
    toString() { return `{x: ${this.from}<=x <=${this.to}}` }
    [Symbol.iterator]() {
        let next = Math.ceil(this.from);
        let last = this.to
        return {
            next() {
                return {
                    done: next > last,
                    value: next++,
                }
            }
        }
    }
}


console.log(new Range(1, 3).toString());
console.log(new Range(1, 3).has(4));

for (const i of new Range(1, 3)) {
    console.log(i);
}

console.log([...new Range(-1, 3)]);




// 定义返回可迭代值的函数
// 替代数组的map方法
function map(iterable, fn) {
    let iterator = iterable[Symbol.iterator]()
    // 返回一个可迭代对象，也是迭代器对象
    return {
        [Symbol.iterator]() { return this },
        next() {
            let obj = iterator.next()
            if (obj.done) {
                return obj
            } else {
                return { value: fn(obj.value) }
            }
        }
    }
}
console.log([...map([1, 2, 3], x => x * x)]);
// 替代数组的filter方法
function filter(iterable, fn) {
    let iterator = iterable[Symbol.iterator]()
    // 返回一个可迭代对象，也是迭代器对象
    return {
        [Symbol.iterator]() { return this },
        next() {
            while (true) {
                let obj = iterator.next()
                if (fn(obj.value) || obj.done) {
                    return obj
                }
            }
        }
    }
}
console.log([...filter([1, 2, 3, 4, 5, 6, 12, 56], x => x > 3)]);

// 懒惰迭代
// 假设一个非常长的字符串想要以空格为分隔,如果使用字符串split方法,那么要处理整个字符串,会占用很多内存来保存返回的数组和其中的字符串.
// 使用懒惰迭代,则不必全部保存在内存
function words(s) {
    let r = /\s+|$/g
    r.lastIndex = s.match(/[^ ]/).index
    return {
        [Symbol.iterator]() { return this },
        next() {
            let start = r.lastIndex
            if (start < s.length) { let match = r.exec(s) if (match) { return { value: s.substring(start, match.index) } } }
            return { done: true }
        }
    }
} console.log([...words(" abc def ghi ")]);